#ifndef OBR_SHADER_ONB_H
#define OBR_SHADER_ONB_H

#include "core/Matrix4x4f.h"

namespace OBR
{
struct Onb
{
    __forceinline__ __device__ Onb(const float3 &normal) : w2l(1.0f), l2w(1.0f)
    {

        float3 m_tangent;
        float3 m_binormal;
        float3 m_normal;
        m_normal = normal;

        if (fabs(m_normal.x) > fabs(m_normal.z))
        {
            m_binormal.x = -m_normal.y;
            m_binormal.y = m_normal.x;
            m_binormal.z = 0;
        }
        else
        {
            m_binormal.x = 0;
            m_binormal.y = -m_normal.z;
            m_binormal.z = m_normal.y;
        }

        m_binormal = normalize(m_binormal);
        m_tangent = cross(m_binormal, m_normal);

        this->l2w.data[0] = make_float4(m_tangent.x, m_binormal.x, m_normal.x, 0);
        this->l2w.data[1] = make_float4(m_tangent.y, m_binormal.y, m_normal.y, 0);
        this->l2w.data[2] = make_float4(m_tangent.z, m_binormal.z, m_normal.z, 0);
        this->w2l = this->l2w.transpose();
    }

    __forceinline__ __device__ float3 local2world(float3 &p)
    {
        return this->l2w * p;
    }

    __forceinline__ __device__ float3 world2local(float3 &p)
    {
        return this->w2l * p;
    }
    Matrix4x4f l2w, w2l;
};

static __forceinline__ __device__ void coordinateSystem(float3 &v1, float3 &v2, float3 &v3)
{
    if (fabsf(v1.x) > fabsf(v1.y))
        v2 = make_float3(-v1.z, 0, v1.x) / sqrtf(v1.x * v1.x + v1.z * v1.z);
    else
        v2 = make_float3(0, v1.z, -v1.y) / sqrtf(v1.y * v1.y + v1.z * v1.z);
    v3 = cross(v1, v2);
}
} // namespace OBR

#endif